/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.misc.rmc.binding;

import org.vacoor.nothing.misc.rmc.session.Session;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * 接口代理
 *
 * @author vacoor
 */
public class MapperProxy<I> implements InvocationHandler {
    private Class<I> mapperInterface;   // 被代理接口
    private Session session;            //
    private Map<Method, MapperMethod<I>> methodCache;   // 被代理方法缓存

    public MapperProxy(Class<I> mapperInterface, Session session, Map<Method, MapperMethod<I>> methodCache) {
        this.mapperInterface = mapperInterface;
        this.session = session;
        this.methodCache = methodCache;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Object 中的方法不代理
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        return cachedMapperMethod(method).execute(args);
    }

    /**
     * 缓存并执行 mapper 方法
     *
     * @param method
     * @return
     */
    protected MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = createMapperMethod(mapperInterface, method, session);
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }

    /**
     * 为给定接口的方法创建一个代理方法
     *
     * @param mapperInterface
     * @param method
     * @param session
     * @return
     */
    protected MapperMethod<I> createMapperMethod(Class<I> mapperInterface, Method method, Session session) {
        try {
            Class<? extends MapperMethod> clazz = findMapperMethodClass(mapperInterface);
            if (null == clazz) {
                throw new IllegalStateException(String.format("can't find %s on %s", MethodProxy.class, mapperInterface));
            }

            Constructor<?> ctor = clazz.getConstructor(Class.class, Method.class, Session.class);
            return (MapperMethod) ctor.newInstance(mapperInterface, method, session);
        } catch (IllegalAccessException e) {
            throw new BindingException(e);
        } catch (NoSuchMethodException e) {
            throw new BindingException(e);
        } catch (InstantiationException e) {
            throw new BindingException(e);
        } catch (InvocationTargetException e) {
            throw new BindingException(e);
        }
    }

    protected Class<? extends MapperMethod> findMapperMethodClass(Class<I> mapperInterface) {
        MethodProxy config = mapperInterface.getAnnotation(MethodProxy.class);
        if (null != config) {
            return config.value();
        }

        Annotation[] annotations = mapperInterface.getAnnotations();
        if (null == annotations || 1 > annotations.length) {
            return null;
        }
        // 遍历所有注解
        for (Annotation annotation : annotations) {
            Class<? extends Annotation> aClass = annotation.annotationType();
            config = aClass.getAnnotation(MethodProxy.class);
            if (null != config) {
                return config.value();
            }
        }
        return null;
    }
}
